
/**
 * Copyright 2009 cestum.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
package org.waveprotocol.wave.model.document.operation.algorithm.transformer {
import org.waveprotocol.wave.model.document.operation.algorithm.transformer.ServerAnnotationTracker;
import org.waveprotocol.wave.model.document.operation.AnnotationBoundaryMap;
import org.waveprotocol.wave.model.document.operation.BufferedDocOp;
import org.waveprotocol.wave.model.document.operation.EvaluatingDocOpCursor;
import org.waveprotocol.wave.model.document.operation.algorithm.OperationNormalizer;
import org.waveprotocol.wave.model.document.operation.algorithm.ValueUpdate;
import org.waveprotocol.wave.model.document.operation.algorithm.transformer.tracker.PositionTracker;
import org.waveprotocol.wave.model.document.operation.impl.AnnotationBoundaryMapImpl;
import org.waveprotocol.wave.model.document.operation.impl.DocOpBuffer;
import org.waveprotocol.wave.model.operation.OperationException;
import org.waveprotocol.wave.model.operation.OperationPair;

/**
 * A utility class for transforming document operations.
 */
public final class Transformer {
	
	static var retainResolver:RangeResolver= new RetainRangeResolver();
	static var deleteElementEndResolver:RangeResolver= new DelElementEndRangeResolver();
	private var clientOperation:EvaluatingDocOpCursor= OperationNormalizer.createNormalizer(new DocOpBuffer());
	private var serverOperation:EvaluatingDocOpCursor= OperationNormalizer.createNormalizer(new DocOpBuffer());
	
	private var clientAnnotationProcessor:AnnotationProcessor= new AnnotationProcessor(clientOperation);
	
	private var serverAnnotationProcessor:AnnotationProcessor= new AnnotationProcessor(serverOperation);
	
	private var clientAnnotationTracker:AnnotationTracker=  
		new ClientAnnotationTracker(clientAnnotationProcessor,serverAnnotationProcessor, serverAnnotationTracker);
	
	private var serverAnnotationTracker:AnnotationTracker= 
		new ServerAnnotationTracker(serverAnnotationProcessor, clientAnnotationProcessor,clientAnnotationTracker);
	
	/**
	 * Transform a pair of operations.
	 */
	public function transformOperations(clientOp:BufferedDocOp,
										serverOp:BufferedDocOp):OperationPair {
		try {
			var positionTracker:PositionTracker= new PositionTracker();
			
			var clientPosition:RelativePosition= positionTracker.getClientPosition();
			var serverPosition:RelativePosition= positionTracker.getServerPosition();
			
			var clientTarget:Target= new Target(this, clientOperation, clientPosition, clientAnnotationTracker);
			var serverTarget:Target= new Target(this, serverOperation, serverPosition, serverAnnotationTracker);
			
			clientTarget.setOtherTarget(serverTarget);
			serverTarget.setOtherTarget(clientTarget);
			var clientIndex:int= 0;
			var serverIndex:int= 0;
			while (clientIndex < clientOp.size()) {
				clientOp.applyComponent(clientIndex++, clientTarget);
				while (clientPosition.getPos() > 0) {
					serverOp.applyComponent(serverIndex++, serverTarget);
				}
			}
			while (serverIndex < serverOp.size()) {
				serverOp.applyComponent(serverIndex++, serverTarget);
			}
			clientOp = clientTarget.finish();
			serverOp = serverTarget.finish();
		} catch (e:TransformError) {
			throw new Error(e.getMessage());
		}
		return new OperationPair(clientOp, serverOp);
	}
	
	/**
	 * Transform a pair of operations.
	 */
	public static function transform(clientOp:BufferedDocOp,
									 serverOp:BufferedDocOp):OperationPair  {
		return new Transformer().transformOperations(clientOp, serverOp);
	}
	
}
}